package com.lixun.passwordbook.slice;

import com.lixun.passwordbook.MainAbility;
import com.lixun.passwordbook.ResourceTable;
import com.lixun.passwordbook.pojo.Colors;
import com.lixun.passwordbook.pojo.Constant;
import com.lixun.passwordbook.util.FileData;
import com.lixun.passwordbook.util.AESUtils;
import com.lixun.passwordbook.util.FaceAuthResult;
import com.lixun.passwordbook.util.LogUtils;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.DataAbilityHelper;
import ohos.aafwk.ability.DataAbilityRemoteException;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.window.dialog.ToastDialog;
import ohos.agp.window.service.WindowManager;
import ohos.app.Context;
import ohos.biometrics.authentication.BiometricAuthentication;
import ohos.data.dataability.DataAbilityPredicates;
import ohos.data.resultset.ResultSet;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.utils.net.Uri;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class HomeAbilitySlice extends AbilitySlice {
    static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, "HomeStartTime");
    private final String FACE_KEY="xswlawsl!lspyyds";
    private static final int POOL_CORE_SIZE = 2;
    private static final int POOL_MAX_SIZE = 5;
    private static final int NO_FACE_RET = -1;
    private static final int KEEP_ALIVE_TIME = 3;
    private static final int QUEUE_SIZE = 6;
    private static final int RET_NOT_SUPPORTED = 1;
    private static final int RET_SAFE_LEVEL_NOT_SUPPORTED = 2;
    private static final int RET_NOT_LOCAL = 3;
    private static final String BASE_URI = "dataability:///com.lixun.passwordbook.DataAbility";
    private static final String DATA_PATH = "/pwb";
    private static final String DB_COLUMN_PASSWOED = "password";
    private static final String DB_COLUMN_ID = "id";
    private DataAbilityHelper databaseHelper;
    private MyEventHandle myEventHandle;
    private BiometricAuthentication mBiometricAuthentication;
    private Context mContext;
    /**
     * 新建线程进行认证，避免阻塞其他任务。
     */
    private final Runnable runnable = new Runnable() {
        private void initHandler() {
            EventRunner runner = EventRunner.getMainEventRunner();
            if (runner == null) {
                return;
            }
            myEventHandle = new MyEventHandle(runner);
        }

        @Override
        public void run() {
            // 初始化myEventHandle
            initHandler();
            // 开始认证
            startAuth();
        }
    };

    /**
     * onStart
     *
     * @param intent intent
     */
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_home);
        mContext = this;
        databaseHelper = DataAbilityHelper.creator(this);
        // 创建确认按钮
        createStartBtn();
        // 设置颜色模式
        colorMode();
        //设置状态栏颜色
        setStatusBarColor();
    }

    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }

    @Override
    protected void onBackground() {
        super.onBackground();
    }

    @Override
    protected void onInactive() {
        super.onInactive();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    /**
     * 设置状态栏颜色
     */
    private void setStatusBarColor() {
        getWindow().addFlags(WindowManager.LayoutConfig.MARK_TRANSLUCENT_NAVIGATION);
        getWindow().setStatusBarColor(Color.TRANSPARENT.getValue());
    }

    /**
     * 设置颜色模式
     */
    private void colorMode(){
        DirectionalLayout abilityHome = (DirectionalLayout) findComponentById(ResourceTable.Id_ability_home);
        Text text = (Text) findComponentById(ResourceTable.Id_ability_home_text);
        Text textFace = (Text) findComponentById(ResourceTable.Id_text_status);
        TextField text1 = (TextField) findComponentById(ResourceTable.Id_home_password);
        boolean dark_mode = Colors.getDarkMode();
        ShapeElement shapeElement = new ShapeElement();
        ShapeElement element;
        Color color;
        if (dark_mode){
            HiLog.info(LABEL, "设置为深色模式");
            shapeElement.setRgbColor(Colors.backgroundColorDark);
            element = new ShapeElement(mContext,ResourceTable.Graphic_detailed_text_field_dark);
            color = Colors.textColorDark;
        } else {
            HiLog.info(LABEL, "设置为常规模式");
            shapeElement.setRgbColor(Colors.backgroundColor);
            element = new ShapeElement(mContext,ResourceTable.Graphic_detailed_text_field);
            color = Colors.textColor;
        }
        abilityHome.setBackground(shapeElement);
        text.setTextColor(color);
        text1.setTextColor(color);
        text1.setBackground(element);
        textFace.setBackground(shapeElement);
    }

    /**
     * 创建开始识别的按钮点击事件
     */
    private void createStartBtn() {
        // 创建点击事件
        Component component = findComponentById(ResourceTable.Id_button_start);
        Context context = getApplicationContext();
        FileData fileData = new FileData();
        boolean face_switch = fileData.getBoolean(context,"face_switch",false);
        // 创建按钮
        Button featureBtn ;
        if (component instanceof Button) {
            featureBtn = (Button) component;
            featureBtn.setClickedListener(view -> {
                if (matchThePWD()){
                    toMain();
                } else if (face_switch){
                    //如果面部识别正在进行，取消面部识别
                    if (mBiometricAuthentication != null) {
                        // 调用取消接口
                        int result = mBiometricAuthentication.cancelAuthenticationAction();
                        LogUtils.info("createCancelBtn:", result + "");
                    }
                    //开始面部识别
                    createStartListener();
                }
            });
        }
    }

    /**
     * 判断启动密码是否正确
     * @return 判断结果
     */
    private boolean matchThePWD(){
        Text password = (Text)findComponentById(ResourceTable.Id_home_password);
        String pwd = password.getText();
        Context context = getApplicationContext();
        FileData fileData = new FileData();
        boolean first = fileData.getBoolean(context,"first",true);
        String pwd1 = fileData.getString(context,"lxpwd","");
        boolean face_switch = fileData.getBoolean(context,"face_switch",false);
        HiLog.info(LABEL,pwd+":"+pwd1+":::"+first);
        String results;
        if (face_switch){
            results = "密码错误，自动启动面部识别。";
        } else {
            results = "密码错误，请重新输入。";
        }
        if (pwd == null || pwd.isEmpty()){
        } else{
            String newpwd = stringToMD5(pwd);
            if(newpwd.equals(pwd1)){
                Constant.setKEY(pwd);
                return true;
            }
        }
        //提醒
        new ToastDialog(this)
                .setText(results)
                // Toast显示在界面中间
                .setAlignment(LayoutAlignment.CENTER)
                .show();
        return false;
    }

    /**
     * stringToMD5
     * @param plainText 需要加密的数据
     * @return 加密后的数据
     */
    public static String stringToMD5(String plainText) {
        byte[] secretBytes = null;
        String newText = plainText + Constant.SALT;
        try {
            secretBytes = MessageDigest.getInstance("md5").digest(
                    newText.getBytes());
            HiLog.info(LABEL,"md5 yes");
        } catch (NoSuchAlgorithmException e) {
            HiLog.info(LABEL,"没有这个md5算法");
            throw new RuntimeException("没有这个md5算法！");
        }
        String md5code = new BigInteger(1, secretBytes).toString(16);
        HiLog.info(LABEL,"md5长度"+md5code.length());
        HiLog.info(LABEL,"md5"+md5code);
        for (int i = 0; i < 32 - md5code.length(); i++) {
            md5code = "0" + md5code;
        }
        HiLog.info(LABEL,"2md5长度"+md5code.length());
        HiLog.info(LABEL,"2md5"+md5code);
        return md5code;
    }

    /**
     * 面部识别功能
     */
    private void createStartListener() {
        // 提示用户人脸识别时将人脸对准摄像头
        getAndSetText(ResourceTable.Id_text_status, NO_FACE_RET);
        try {
            // 创建生物识别对象
            mBiometricAuthentication = BiometricAuthentication.getInstance(MainAbility.getMainAbility());
            // 检验设备是否有人脸识别功能
            // BiometricAuthentication.AuthType中有三个类别
            // 分别为AUTH_TYPE_BIOMETRIC_FINGERPRINT_ONLY指纹识别
            // AUTH_TYPE_BIOMETRIC_FACE_ONLY脸部识别
            // AUTH_TYPE_BIOMETRIC_ALL指纹和面部
            // BiometricAuthentication.SecureLevel 2D人脸识别建议使用SECURE_LEVEL_S2，3D人脸识别建议使用SECURE_LEVEL_S3
            int hasAuth = mBiometricAuthentication.checkAuthenticationAvailability(
                    BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY,
                    BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2, true);
            // hasAuth 0是支持，1是不支持，2安全级别不支持 3不是本地认证 4无人脸录入
            if (hasAuth == 0) {
                ThreadPoolExecutor pool = new ThreadPoolExecutor(
                        POOL_CORE_SIZE, POOL_MAX_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS,
                        new LinkedBlockingQueue<>(QUEUE_SIZE), new ThreadPoolExecutor.DiscardOldestPolicy());
                pool.submit(runnable);
            } else {
                // 人脸识别不支持或存在其他问题 ，直接回显页面，
                // 在主线程不需要通过EventHandler发送回显任务
                int retExcAuth = getRetExcAuth(hasAuth);
                getAndSetText(ResourceTable.Id_text_status, retExcAuth);
            }
        } catch (IllegalAccessException e) {
            LogUtils.error("createStartBtn", "IllegalAccessException when start auth");
        }
    }

    /**
     * 开始认证
     */
    private void startAuth() {
        // retExcAuth 0认证成功 1：比对失败 2：取消认证 3认证超时 4：打开相机失败
        // 5：busy，可能上一个认证没有结束 6：入参错误 7：人脸认证锁定（达到错误认证次数了)
        // 8：没有录入人脸 100:其他错误。
        int retExcAuth = mBiometricAuthentication.execAuthenticationAction(
                BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY,
                BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2,
                true, false, null);
        // 将修改页面发送到主线程执行
        myEventHandle.sendEvent(retExcAuth);
    }

    /**
     * 根据检验是否支持认证返回值获取提示code
     *
     * @param hasAuth 是否有认证能力
     * @return 返回认证码
     */
    private int getRetExcAuth(int hasAuth) {
        int retExcAuth;
        if (hasAuth == RET_NOT_SUPPORTED) {
            // 1是不支持2D人脸识别
            retExcAuth = FaceAuthResult.AUTH_2D_NOT_SUPPORTED;
        } else if (hasAuth == RET_SAFE_LEVEL_NOT_SUPPORTED) {
            // 安全级别不支持
            retExcAuth = FaceAuthResult.AUTH_SAFE_LEVEL_NOT_SUPPORTED;
        } else if (hasAuth == RET_NOT_LOCAL) {
            // 是不是本地认证
            retExcAuth = FaceAuthResult.AUTH_NOT_LOCAL;
        } else {
            // 无人脸录入
            retExcAuth = FaceAuthResult.AUTH_NO_FACE;
        }
        return retExcAuth;
    }

    /**
     * 获取并设置text
     *  @param textId 文本框id
     * @param retExcAuth 认证返回码
     */
    private void getAndSetText(int textId, int retExcAuth) {
        // 获取状态Text
        Component componentText = findComponentById(textId);
        if (componentText instanceof Text) {
            Text text = (Text) componentText;
            setTextValueAndColor(retExcAuth, text);
            text.setVisibility(Component.VISIBLE);
        }
    }

    /**
     * 设置文本提示信息
     *
     * @param text 文本对象
     * @param textValue 文本值
     * @param color 文本颜色
     */
    private void setTextValueAndColor(Text text, String textValue, Color color) {
        text.setText(textValue);
        text.setTextColor(color);
    }

    /**
     * 设置文本显示值和文本颜色，认证结果
     *
     * @param retExcAuth 认证返回值
     * @param text 文本对象
     */
    private void setTextValueAndColor(int retExcAuth, Text text) {
        switch (retExcAuth) {
            case FaceAuthResult.AUTH_SUCCESS:
                setTextValueAndColor(text, "认证成功", Color.GREEN);
                // 页面跳转
                Constant.setKEY(getData());
                toMain();
                break;
            case FaceAuthResult.AUTH_FAIL:
                setTextValueAndColor(text, "比对失败", Color.RED);
                break;
            case FaceAuthResult.AUTH_CANCLE:
                setTextValueAndColor(text, "取消认证", Color.RED);
                break;
            case FaceAuthResult.AUTH_TIME_OUT:
                setTextValueAndColor(text, "认证超时", Color.RED);
                break;
            case FaceAuthResult.AUTH_OPEN_CAMERA_FAIL:
                setTextValueAndColor(text, "打开相机失败", Color.RED);
                break;
            case FaceAuthResult.AUTH_BUSY:
                setTextValueAndColor(text, "busy，可能上一个认证没有结束", Color.RED);
                break;
            case FaceAuthResult.AUTH_PARAM_ERROR:
                setTextValueAndColor(text, "入参错误", Color.RED);
                break;
            case FaceAuthResult.AUTH_FACE_LOCKED:
                setTextValueAndColor(text, "人脸认证锁定", Color.RED);
                break;
            case FaceAuthResult.AUTH_NO_FACE:
                setTextValueAndColor(text, "无人脸数据。", Color.BLUE);
                break;
            case FaceAuthResult.AUTH_OTHER_ERROR:
                setTextValueAndColor(text, "其他错误。", Color.RED);
                break;
            case FaceAuthResult.AUTH_2D_NOT_SUPPORTED:
                setTextValueAndColor(text, "不支持2D人脸识别。", Color.BLUE);
                break;
            case FaceAuthResult.AUTH_SAFE_LEVEL_NOT_SUPPORTED:
                setTextValueAndColor(text, "安全级别不支持。", Color.BLUE);
                break;
            case FaceAuthResult.AUTH_NOT_LOCAL:
                setTextValueAndColor(text, "不是本地认证。", Color.BLUE);
                break;
            default:
                setTextValueAndColor(text, "开始面部识别", Color.BLUE);
                break;
        }
    }

    /**
     * 跳转到主界面
     */
    private void toMain() {
        MainAbilitySlice.setIsFirst(false);
        Intent secondIntent = new Intent();
        // 指定待启动FA的bundleName和abilityName
        Operation operation = new Intent.OperationBuilder()
                .withDeviceId("")
                .withBundleName(getBundleName())
                .withAbilityName(MainAbility.class.getName())
                .build();
        secondIntent.setOperation(operation);
        // 通过AbilitySlice的startAbility接口实现启动另一个页面
        startAbility(secondIntent);
        // 销毁Ability
        terminate();
    }

    /**
     * 面部识别通过后获取数据
     * @return 获取的密钥
     */
    private String getData(){
        String[] columns = new String[]{DB_COLUMN_PASSWOED};
        DataAbilityPredicates predicates = new DataAbilityPredicates();
        predicates.equalTo(DB_COLUMN_ID, 1);
        try {
            ResultSet resultSet = databaseHelper.query(Uri.parse(BASE_URI + DATA_PATH),
                    columns, predicates);
            if (resultSet == null || resultSet.getRowCount() == 0) {
                HiLog.info(LABEL, "query: resultSet is null or no result found");
            }
            resultSet.goToFirstRow();
            return AESUtils.decrypt(FACE_KEY,resultSet.getString(resultSet.getColumnIndexForName(DB_COLUMN_PASSWOED)));
        } catch (DataAbilityRemoteException | IllegalStateException exception) {
            HiLog.error(LABEL, "query: dataRemote exception | illegalStateException");
        }
        return null;
    }

    /**
     * 事件分发器
     *
     * @since 2021-04-12
     */
    private class MyEventHandle extends EventHandler {
        MyEventHandle(EventRunner runner) throws IllegalArgumentException {
            super(runner);
        }

        @Override
        protected void processEvent(InnerEvent event) {
            super.processEvent(event);
            int eventId = event.eventId;
            getAndSetText(ResourceTable.Id_text_status, eventId);
        }
    }

}

